#ifndef _GEOMPLANE_
#define _GEOMPLANE_

#include "GeomObject.h"

// plane defined by normal and a constant
class  GeomPlane: public GeomObject{
protected:
	// Plane equation: mNormal.point + mConstant == 0
	Vec3d mNormal;
	double mConstant;
	Vec3d mPt;

	Vec2d mDim;

	Vec3d mBasisU;
	Vec3d mBasisV;

	inline void setBasisVectors(){ 
		if( fabs(mNormal[0]) > fabs(mNormal[1]) ){
			double len = sqrt( mNormal[0]*mNormal[0] + mNormal[2]*mNormal[2] );
			mBasisU = Vec3d( mNormal[2]/len, 0.0, -mNormal[0]/len );
		}else{
			double len = sqrt( mNormal[1]*mNormal[1] + mNormal[2]*mNormal[2] );
			mBasisU = Vec3d( 0.0, mNormal[2]/len, -mNormal[1]/len );
		}
		mBasisV = cross(mNormal, mBasisU); 
		mDim = Vec2d(10,10);
	} 

public:
		// Constructor
		GeomPlane(Vec3d _normal, double _constant = 0.0) : mNormal(norm(_normal)), mConstant(_constant){ 
			mPt = -_constant*_normal;
			setBasisVectors(); 
			mType=PLANE;
		}
		GeomPlane(Vec3d _normal, Vec3d _pt) : mNormal(norm(_normal)){ 
			mPt = _pt;
			mConstant = -dot(mNormal, _pt);
			setBasisVectors(); 
			mType=PLANE;
		}

		inline bool isInside(Vec3d _pt){
			Vec3d _localPt = xform(getInvWorldTransform(), _pt);
			if(getSignedDist(_localPt)<=0) return true;
			else return false;
		}
		// Get signed distance to inPoint
		inline double getSignedDist(const Vec3d &inPoint){
			Vec3d _localPt = xform(getInvWorldTransform(), inPoint);
			return dot(_localPt, mNormal) + mConstant;
		}
		double getVolume(){return 0;}
		void setExtent(Vec3d _d){mDim[0]=_d[0]; mDim[1]=_d[1];}

		// Get two vectors that together with mNormal form a basis for the plane
		inline void getBasisVectors( Vec3d &inU, Vec3d &inV ){
			inU = mBasisU;
			inV = mBasisV;
		}		
		inline Vec3d getNormal(){ return mNormal; }
		inline double getConst(){ return mConstant; }
		inline Vec3d getPoint(){ return mPt; }

		//// Convert a point from world space to plane space (3D -> 2D)
		//// assume the point is on this plane
		//Vec2d transformWorldToLocal(const Vec3d inPoint){
		//	return Vec2d(dot(mBasisU, inPoint), dot(mBasisV, inPoint));
		//}

		//// Convert a point from plane space to world space (2D -> 3D)
		//Vec3d transformLocalToWorld( const Vec2d inPoint ){
		//	return Vec3d( mBasisU * inPoint[0] + mBasisV * inPoint[1] - mNormal * mConstant);
		//}

		//// Get matrix that converts a point from plane space to world space (2D -> 3D)
		//// v3D = mat * (v2D , 1)
		//Mat3d getLocalToWorldMatrix();

		void draw(Vec4d _col=Vec4d(vl_1), bool _default=true);
		// Transform a plane by the inverse of inInverseMatrix
		GeomObject* getObjApplyInvTransform(const Mat4d &_mat);

};

#endif
